package com.re6g3y.game.mulitThread;

import com.badlogic.gdx.utils.Disposable;
import com.re6g3y.game.mulitThread.Actions.NodeAction;

import java.util.concurrent.*;


/**
 * 阻塞式多线程
 * add() 单线程入口分配任务
 *
 * 上面用于分配任务时，找到任务的时间较长，可以边加任务边执行任务（先notify在加任务）
 * notifyAllThread() 唤起线程一有任务就运行,否者cpu自旋
 * waitForFinish() 全部执行完成后跳出
 *
 * 下面这个建议用于任务cpu时间短的（尽量让cpu少自旋）
 * notifyAndWaitFinish()
 *
 * dispose() 释放线程
 * 任务分配者，Node为最小任务
 */
public class MainAssigner implements Disposable {
    private Thread[] threadPool;
    private ConcurrentLinkedQueue<Object> queue;//可替换成线程安全的任意队列
    private int sumAdd[];
    private int maxNodeNumber = 0;
    private volatile boolean iswaitForFinish = true;
    private volatile boolean iswaitForDispose = false;

    private boolean isAllFinish() {
        int ans = 0;
        synchronized (this) {
            for (int i = 0; i < sumAdd.length; i++) {
                ans += sumAdd[i];
//            System.out.print(sumAdd[i]+" ");
            }
//        System.out.println(ans+"=="+maxNodeNumber);
            return maxNodeNumber == ans;
        }
    }


    /**
     * 等待全部线程结束，同步等待
     * 全部线程进入 wait 状态
     */
    public void waitForFinish(){
        try {
            synchronized (this) {
                iswaitForFinish = true;
                while (!isAllFinish()) {
                        this.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }/**
     * 全部线程进入 running 状态
     */
    public void notifyAllThread(){
        iswaitForFinish = false;
        for(int i=0;i<threadPool.length;i++){
            synchronized (threadPool[i]){
                threadPool[i].notify();
            }
        }
    }

    /**
     * 执行并等待结束
     * notifyAllThread();
     * waitForFinish();
     */
    public void notifyAndWaitFinish(){
        notifyAllThread();
        waitForFinish();
    }

    /**
     * 多线程不安全
     * @param node
     */
    final public void add(Object node) {
        maxNodeNumber++;
        queue.add(node);
    }

    final public void run(Object node) {
        NodeAction act = ((Node) node).getNodeAction();
        if (act != null) act.action(node);
        else throw new RuntimeException("NodeAction cannot be null!");
    }

    protected MainAssigner() {
    }

    /**
     * 任务分配者，Node为最小任务
     * 请使用静态全局变量，不要作为局部变量，否则线程数量会爆炸
     * 其次，加完 node 记得调用 waitForFinish等待线程完成
     */
    public MainAssigner(int n) {
        queue = new ConcurrentLinkedQueue<Object>();
        threadPool = new Thread[n];
        sumAdd = new int[n];
        final MainAssigner thisMainAssigner = this;
        for (int i = 0; i < threadPool.length; i++) {
            final int ind = i;
            threadPool[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    int index = ind;
                    try {
                        while(true){
                            while(true){
                                Object node=queue.poll() ;
                                if(node!=null) {
                                    thisMainAssigner.run(node);
                                    sumAdd[index]++;
                                }
                                if(iswaitForFinish&&queue.isEmpty())break;
                            }

                            if (iswaitForDispose) {
                                break;
                            }

                            if(isAllFinish())synchronized (thisMainAssigner) {
                                    thisMainAssigner.notify();
                            }

                            synchronized (threadPool[index]) {
                                threadPool[index].wait();
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            threadPool[i].setName("MainAssigner Thread:" + i);
            threadPool[i].setPriority(3);
            threadPool[i].start();
        }
    }

    /**
     * 内含 waitForFinish
     * 释放线程资源
     * 结束时使用
     */
    public void dispose() {
        iswaitForDispose = true;
        iswaitForFinish = true;//这个一点要放后面
        notifyAllThread();
        waitForFinish();
//        for (int i = 0; i < threadPool.length; i++) {
//            add(new ThreadFinishNode());
//        }
    }
//
//    private static class ThreadFinishNode {
//    }
}